<script lang="ts" setup>
import type { CSSProperties } from 'vue';
import type { NoticeBarExpose } from './types';
import { storeToRefs } from 'pinia';
import { computed, getCurrentInstance, nextTick, onActivated, onDeactivated, onMounted, reactive, ref, watch } from 'vue';
import { getRect, isArray, isDef, objToStyle } from 'wot-design-uni/components/common/util';
import { useAppStore } from '@/store/appStore';
import { noticeBarProps } from './types';

const props = defineProps(noticeBarProps);

const emit = defineEmits(['close', 'next', 'click']);

const $wrap = '.wd-notice-bar__wrap';
const $content = '.wd-notice-bar__content';

const wrapWidth = ref<number>(0);
const show = ref<boolean>(true);
const currentIndex = ref<number>(0);
const textArray = computed(() => (Array.isArray(props.text) ? props.text : [props.text]));
const currentText = computed(() => textArray.value[currentIndex.value]);
const verticalIndex = ref<number>(0);
const wrapRect = ref<UniApp.NodeInfo | null>(null); // 外层容器节点信息
const contentRect = ref<UniApp.NodeInfo | null>(null); // 内容节点信息
const isHorizontal = computed(() => props.direction === 'horizontal');
const isVertical = computed(() => props.direction === 'vertical');

const transitionState = reactive<CSSProperties>({
  transitionProperty: 'unset',
  transitionDelay: 'unset',
  transitionDuration: 'unset',
  transform: 'none',
  transitionTimingFunction: 'linear'
});

const animation = computed(() => {
  return objToStyle(transitionState);
});

const rootStyle = computed(() => {
  const style: CSSProperties = {};
  if (isDef(props.color)) {
    style.color = props.color;
  }

  if (isDef(props.backgroundColor)) {
    style.background = props.backgroundColor;
  }

  return `${objToStyle(style)}${props.customStyle}`;
});
const noticeBarClass = computed(() => {
  const { type, wrapable, scrollable } = props;

  const noticeBarClasses: string[] = [];
  type && noticeBarClasses.push(`is-${type}`);

  if (isHorizontal.value) {
    !wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--ellipse');
  }
  else {
    noticeBarClasses.push('wd-notice-bar--ellipse');
  }

  wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--wrap');
  return noticeBarClasses.join(' ');
});

const { proxy } = getCurrentInstance() as any;

watch(
  () => props.text,
  () => {
    reset();
  },
  { deep: true }
);

onMounted(() => {
  startTransition();
  // #ifdef APP-PLUS
  const pages = getCurrentPages();
  const currentPage = pages[pages.length - 1];
  const currentWebview = currentPage.$getAppWebview!();
  currentWebview.addEventListener('hide', () => {
    stopTransition();
  });
  currentWebview.addEventListener('show', () => {
    startTransition();
  });
  // #endif;
});

onActivated(() => {
  startTransition();
});

onDeactivated(() => {
  stopTransition();
});

function reset() {
  stopTransition();
  startTransition();
}

function startTransition() {
  nextTick(() => scroll());
}

function stopTransition() {
  transitionState.transitionProperty = 'unset';
  transitionState.transitionDelay = 'unset';
  transitionState.transitionDuration = 'unset';
  transitionState.transform = 'none';
  transitionState.transitionTimingFunction = 'linear';
  currentIndex.value = 0;
  verticalIndex.value = 0;
}

function handleClose() {
  show.value = false;
  emit('close');
}

function setTransition({ duration, delay, translate }: { duration: number, delay: number, translate: number }) {
  transitionState.transitionProperty = 'all';
  transitionState.transitionDelay = `${delay}s`;
  transitionState.transitionDuration = `${duration}s`;
  transitionState.transform = `${props.direction === 'vertical' ? 'translateY' : 'translateX'}(${translate}px)`;
  transitionState.transitionTimingFunction = 'linear';
}

// ###########################################################
const { scale } = storeToRefs(useAppStore());

watch(scale, () => {
  startTransition();
}, {
  immediate: true
});

function queryRect() {
  return Promise.all([getRect($wrap, false, proxy), getRect($content, false, proxy)]).then(res => {
    // 修正寸尺
    const [wRect, cRect] = res;
    wRect.width = wRect.width / scale.value;
    wRect.height = wRect.height / scale.value;
    cRect.width = cRect.width / scale.value;
    cRect.height = cRect.height / scale.value;
    return res;
  });
}
// ###########################################################

async function verticalAnimate(height: number) {
  const translate = -(height / (textArray.value.length + 1)) * (currentIndex.value + 1);
  setTransition({
    duration: height / (textArray.value.length + 1) / props.speed,
    delay: props.delay,
    translate
  });
}

async function scroll() {
  const [wRect, cRect] = await queryRect();
  if (!wRect.width || !cRect.width || !cRect.height)
    return;
  wrapRect.value = wRect;
  contentRect.value = cRect;
  wrapWidth.value = wRect.width;

  if (isHorizontal.value) {
    if (props.scrollable) {
      setTransition({
        duration: cRect.width / props.speed,
        delay: props.delay,
        translate: -cRect.width
      });
    }
  }
  else {
    if (textArray.value.length > 1) {
      verticalAnimate(cRect.height);
    }
  }
}

function next() {
  if (currentIndex.value >= textArray.value.length - 1) {
    currentIndex.value = 0;
  }
  else {
    currentIndex.value++;
  }
  emit('next', currentIndex.value);
}

function animationEnd() {
  if (isHorizontal.value) {
    setTransition({
      duration: 0,
      delay: 0,
      translate: wrapWidth.value + 1
    });
  }
  else {
    if (++verticalIndex.value >= textArray.value.length) {
      verticalIndex.value = 0;
      setTransition({
        duration: 0,
        delay: 0,
        translate: 0
      });
    }
  }

  const timer = setTimeout(() => {
    next(); // 更换下一条文本

    nextTick(async () => {
      try {
        const [wRect, cRect] = await queryRect();
        wrapRect.value = wRect;
        contentRect.value = cRect;
        wrapWidth.value = wRect.width || 0;
      }
      // eslint-disable-next-line unused-imports/no-unused-vars
      catch (_) {
        // console.error(error);
      }

      if (!contentRect.value || !contentRect.value.width || !contentRect.value.height)
        return;

      if (isHorizontal.value) {
        setTransition({
          duration: (wrapWidth.value + contentRect.value.width) / props.speed,
          delay: props.delay,
          translate: -contentRect.value.width
        });
      }
      else {
        verticalAnimate(contentRect.value.height);
      }
    });

    clearTimeout(timer);
  }, 20);
}

function handleClick() {
  const result = isArray(props.text)
    ? {
        index: currentIndex.value,
        text: props.text[currentIndex.value]
      }
    : {
        index: 0,
        text: props.text
      };
  emit('click', result);
}

defineExpose<NoticeBarExpose>({ reset });
</script>

<template>
  <view v-if="show" :class="`wd-notice-bar ${customClass} ${noticeBarClass}`" :style="rootStyle">
    <wd-icon v-if="prefix" custom-class="wd-notice-bar__prefix" :name="prefix" />
    <slot v-else name="prefix" />
    <view class="wd-notice-bar__wrap">
      <view class="wd-notice-bar__content" :style="animation" @transitionend="animationEnd" @click="handleClick">
        <template v-if="isVertical">
          <view v-for="item in textArray" :key="item">
            {{ item }}
          </view>
          <view v-if="textArray.length > 1">
            {{ textArray[0] }}
          </view>
        </template>
        <slot v-else>
          {{ currentText }}
        </slot>
      </view>
    </view>
    <wd-icon v-if="closable" custom-class="wd-notice-bar__suffix" name="close-bold" @click="handleClose" />
    <slot v-else name="suffix" />
  </view>
</template>

<style lang="scss" scoped>
@import './index.scss';
</style>
